package cn.acheng1314.im.common.server;


import cn.acheng1314.im.common.client.CountInfo;
import cn.acheng1314.im.common.enums.ServerType;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.handler.timeout.IdleStateHandler;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.EventListener;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 抽象服务器，默认配置为8核16g
 */
public abstract class AbstractServer {

    /**
     * 服务类型，默认是socket服务器
     */
    private ServerType serverType = ServerType.SOCKET;

    /**
     * 绑定端口，默认为8000
     */
    protected int port = 8000;
    /**
     * 多IP情况下绑定指定的IP(可以不设置)
     */
    protected String ip;
    /**
     * 是否启用keepAlive
     */
    protected boolean keepAlive = true;
    /**
     * 是否启用tcpNoDelay
     */
    protected boolean tcpNoDelay = true;
    /**
     * 工作线程池大小
     */
    protected int workerCount;

    /**
     * 是否开户业务处理线程池
     */
    protected boolean openExecutor = false;
    /**
     * 消息事件业务处理线程池
     */
    protected ExecutorService messageExecutor;
    /**
     * 通道事件业务处理线程池
     */
    protected ExecutorService channelExecutor;
    /**
     * 异常事件业务处理线程池
     */
    protected ExecutorService exceptionExecutor;
    /**
     * 消息事件业务处理线程池最小保持的线程数
     */
    protected int corePoolSize = 10;
    /**
     * 消息事件业务处理线程池最大线程数
     */
    protected int maximumPoolSize = 150;
    /**
     * 消息事件业务处理线程池队列最大值
     */
    protected int queueCapacity = 1000000;
    /**
     * 设置是否心跳检查
     */
    protected boolean checkHeartbeat = true;
    /**
     * 心跳检查时的读空闲时间
     */
    protected int readerIdleTimeSeconds = 0;
    /**
     * 心跳检查时的写空闲时间
     */
    protected int writerIdleTimeSeconds = 0;
    /**
     * 心跳检查时的读写空闲时间
     */
    protected int allIdleTimeSeconds = 90;

    /**
     * 客户端链接池，所有用户均在此获取
     */
    protected ConcurrentHashMap<String, Channel> channels = new ConcurrentHashMap<>();

    protected EventLoopGroup bossGroup;

    protected EventLoopGroup workerGroup;

    protected CountInfo countInfo = new CountInfo();

    protected IdleStateHandler timeoutHandler;

    protected LinkedHashMap<String, ChannelHandler> handlers = new LinkedHashMap<>();

    protected List<EventListener> eventListeners = new ArrayList<>();

    public void init() {
        if (useEpoll()) {
            bossGroup = new EpollEventLoopGroup(workerCount, new ThreadFactory() {
                private final AtomicInteger index = new AtomicInteger(0);

                @Override
                public Thread newThread(Runnable r) {
                    return new Thread(r, "Epoll_BOSS_" + index.incrementAndGet());
                }
            });
            workerGroup = new EpollEventLoopGroup(workerCount, new ThreadFactory() {
                private final AtomicInteger index = new AtomicInteger(0);

                @Override
                public Thread newThread(Runnable r) {
                    return new Thread(r, "Epoll_WORK_" + index.incrementAndGet());
                }
            });
        } else {
            bossGroup = new NioEventLoopGroup(workerCount, new ThreadFactory() {
                private final AtomicInteger index = new AtomicInteger(0);

                @Override
                public Thread newThread(Runnable r) {
                    return new Thread(r, "Nio_BOSS_" + index.incrementAndGet());
                }
            });
            workerGroup = new NioEventLoopGroup(workerCount, new ThreadFactory() {
                private final AtomicInteger index = new AtomicInteger(0);

                @Override
                public Thread newThread(Runnable r) {
                    return new Thread(r, "Nio_WORK_" + index.incrementAndGet());
                }
            });
        }
    }

    /**
     * 构建服务器
     */
    public abstract void bind();


    /**
     * shutdown
     */
    public abstract void shutdown();

    /**
     * 是否开启linux专用EpollEventLoopGroup
     *
     * @return
     */
    public boolean useEpoll() {
        String osName = System.getProperty("os.name");
        boolean isLinuxPlatform = StringUtils.containsIgnoreCase(osName, "linux");
        return isLinuxPlatform && io.netty.channel.epoll.Epoll.isAvailable();
    }

    public ServerType getServerType() {
        return serverType;
    }

    public void setServerType(ServerType serverType) {
        this.serverType = serverType;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public boolean isKeepAlive() {
        return keepAlive;
    }

    public void setKeepAlive(boolean keepAlive) {
        this.keepAlive = keepAlive;
    }

    public boolean isTcpNoDelay() {
        return tcpNoDelay;
    }

    public void setTcpNoDelay(boolean tcpNoDelay) {
        this.tcpNoDelay = tcpNoDelay;
    }

    public int getWorkerCount() {
        return workerCount;
    }

    public void setWorkerCount(int workerCount) {
        this.workerCount = workerCount;
    }

    public boolean isOpenExecutor() {
        return openExecutor;
    }

    public void setOpenExecutor(boolean openExecutor) {
        this.openExecutor = openExecutor;
    }

    public ExecutorService getMessageExecutor() {
        return messageExecutor;
    }

    public void setMessageExecutor(ExecutorService messageExecutor) {
        this.messageExecutor = messageExecutor;
    }

    public ExecutorService getChannelExecutor() {
        return channelExecutor;
    }

    public void setChannelExecutor(ExecutorService channelExecutor) {
        this.channelExecutor = channelExecutor;
    }

    public ExecutorService getExceptionExecutor() {
        return exceptionExecutor;
    }

    public void setExceptionExecutor(ExecutorService exceptionExecutor) {
        this.exceptionExecutor = exceptionExecutor;
    }

    public int getCorePoolSize() {
        return corePoolSize;
    }

    public void setCorePoolSize(int corePoolSize) {
        this.corePoolSize = corePoolSize;
    }

    public int getMaximumPoolSize() {
        return maximumPoolSize;
    }

    public void setMaximumPoolSize(int maximumPoolSize) {
        this.maximumPoolSize = maximumPoolSize;
    }

    public int getQueueCapacity() {
        return queueCapacity;
    }

    public void setQueueCapacity(int queueCapacity) {
        this.queueCapacity = queueCapacity;
    }

    public boolean isCheckHeartbeat() {
        return checkHeartbeat;
    }

    public void setCheckHeartbeat(boolean checkHeartbeat) {
        this.checkHeartbeat = checkHeartbeat;
    }

    public int getReaderIdleTimeSeconds() {
        return readerIdleTimeSeconds;
    }

    public void setReaderIdleTimeSeconds(int readerIdleTimeSeconds) {
        this.readerIdleTimeSeconds = readerIdleTimeSeconds;
    }

    public int getWriterIdleTimeSeconds() {
        return writerIdleTimeSeconds;
    }

    public void setWriterIdleTimeSeconds(int writerIdleTimeSeconds) {
        this.writerIdleTimeSeconds = writerIdleTimeSeconds;
    }

    public int getAllIdleTimeSeconds() {
        return allIdleTimeSeconds;
    }

    public void setAllIdleTimeSeconds(int allIdleTimeSeconds) {
        this.allIdleTimeSeconds = allIdleTimeSeconds;
    }

    public ConcurrentHashMap<String, Channel> getChannels() {
        return channels;
    }

    public void setChannels(ConcurrentHashMap<String, Channel> channels) {
        this.channels = channels;
    }

    public EventLoopGroup getBossGroup() {
        return bossGroup;
    }

    public void setBossGroup(EventLoopGroup bossGroup) {
        this.bossGroup = bossGroup;
    }

    public EventLoopGroup getWorkerGroup() {
        return workerGroup;
    }

    public void setWorkerGroup(EventLoopGroup workerGroup) {
        this.workerGroup = workerGroup;
    }

    public CountInfo getCountInfo() {
        return countInfo;
    }

    public void setCountInfo(CountInfo countInfo) {
        this.countInfo = countInfo;
    }
}
